還記得我們在前一個章節中提到的 Grafana as Code 我個人理想組合是 Terraform 和 Jsonnet 嗎?在一開始也提到透過高可讀性的 JSON 設定檔,我們可以輕鬆設置幾乎所有 Grafana 的內部資源,這看似是一個美好的解決方案。
然而,隨著團隊和業務規模的擴大,Dashboard 的數量越來越多,我們逐漸發現其中的設定開始變得重複、冗長,並且難以維護和管理。每次修改設定時,都可能涉及到大量的複製與修改,這不僅影響效率,還增加了出錯的風險。在這樣的背景下,Jsonnet 作為一種進階的設定管理工具,成為了 Grafana 團隊應對這一挑戰的終極解決方案。
接下來,我們將深入探討大家熟悉的 JSON 和 Jsonnet 之間的具體差異,並聊聊為何 Jsonnet 能夠在 Grafana 生態系統中占據如此重要的一席之地。
Jsonnet 是一種強大而靈活且圖靈具備的資料模板程式語言,它是 JSON 的更多進階功能的擴充版本,旨在幫助使用者生成複雜的 JSON 資料。在探討 Jsonnet 的特性之前,我們需要理解為什麼需要這樣一種語言。
在現代軟體開發中,設定管理變得越來越重要。隨著系統規模的擴大和複雜度的提升,我們需要一種既能保持簡潔易讀,又能處理複雜邏輯的設置語言。JSON 作為一種廣泛使用的資料交換格式,雖然簡單直觀,但在處理大型、複雜的設定時顯得力不從心。這就是 Jsonnet 誕生的背景。
Jsonnet 提供了許多 JSON 所不具備的特性:
這些特性使得 Jsonnet 成為一種既保留了 JSON 簡潔性,又具備程式語言靈活性的強大工具。它的主要目標是減少重複程式碼,提高可維護性,並使大型設定檔更易於管理。
在深入探討 Jsonnet 的優勢之前,我們有必要回顧一下 JSON 的應用場景及其侷限性。JSON(JavaScript Object Notation)作為一種輕量級的資料交換格式,自誕生以來就因其簡潔性和易用性而廣受歡迎。
它在許多領域都有廣泛的應用,包括但不限於:
JSON 的優點顯而易見:
然而,隨著系統複雜度的增加,JSON 也暴露出了一些明顯的缺陷:
這些限制在小型專案中可能不太明顯,但在大規模系統中,特別是在需要頻繁更新和維護的場景下,它們會成為嚴重的障礙。這就是為什麼我們需要一種更強大的工具來處理複雜的設定需求,而 Jsonnet 正是為解決這些問題而生的。
Jsonnet 是一種基於 JSON 的編程語言,提供了更多靈活性和可讀性。它支援註解、引用、邏輯和算術運算、數組和物件的深入操作,並能通過模組化來提高可維護性。以下是 Jsonnet 的一些常見使用方式及範例:
MacOs:
$ brew install jsonnet
Golang:
$ go get github.com/google/go-jsonnet/cmd/jsonnet
將簡單的表達式保存至檔案並執行:
$ echo '{key: 1 + 2}' > test.jsonnet
$ jsonnet test.jsonnet
{
"key": 3
}
也可以直接使用 -e
選項來運行簡單的程式碼:
$ jsonnet -e '{key: 1 + 2}'
{
"key": 3
}
格式化 Jsonnet 原始碼:
$ jsonnet fmt test.jsonnet
註解(支援單行和多行註解):
/*
multiple line comment
*/
{ // single line comment
key1: {
"user": [
{kind: "person", value: 1.0},
]
}
}
引用(使用 self
引用當前對象,$
引用根對象):
{
example: {
data: "sample",
reference: self.data
},
rootExample: {
data: $.example.data
}
}
資料運算(支援邏輯和算術運算):
{
num: 4,
multiplied: 2 * self.num,
message: "The value is " + self.multiplied + ".",
concatenatedArray: [1, 2, 3] + [4, 5], // Array
isEqual: 1 == 2
}
陣列和物件的深入操作:
{
nums: [1, 2, 3],
squared: [x * x for x in self.nums if x >= 2],
fields: {["field" + x]: x for x in self.nums},
obj: {["prefix" + "suffix"]: 3},
}
函數:
// Function
{
calculate(size, values)::
if std.length(values) == 0 then
error "No data provided"
else [
{kind: i, result: size / std.length(values)}
for i in values
],
identity:: function(x) x,
}
local Person(name='Alice') = {
name: name,
greeting: 'Hello ' + name + '!',
};
{
person1: Person(),
person2: Person('Bob'),
}
變數:
// Variable
{
local myData = "sample",
data: {
example: myData
}
}
物件導向繼承:
{
base: {
value: "baseValue",
},
derived: self["base"] + {
additional: "extraValue"
}
}
為了更好地理解 Jsonnet 如何解決 JSON 的局限性,讓我們通過一個具體的例子來展示 Jsonnet 的強大之處。假設我們正在為一個監控系統創建 Grafana Dashboard 設定。使用傳統的 JSON,我們可能會這樣寫:
{
"dashboard": {
"title": "My Dashboard",
"panels": [
{
"title": "CPU Usage",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "avg(rate(node_cpu_seconds_total{mode=\"user\"}[5m])) * 100",
"legendFormat": "CPU Usage"
}
]
},
{
"title": "Memory Usage",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100",
"legendFormat": "Memory Usage"
}
]
}
]
}
}
這個 JSON 設定雖然直觀,但如果我們需要添加更多類似的 Panel,就會導致大量的重複程式碼。現在,讓我們看看如何使用 Jsonnet 來改進這個設定:
{
local dashboard = {
title: "My Dashboard",
panels: [],
},
local createPanel(title, metric) = {
title: title,
type: "graph",
datasource: "Prometheus",
targets: [
{
expr: metric,
legendFormat: title,
},
],
},
dashboard: dashboard {
panels: [
createPanel("CPU Usage", 'avg(rate(node_cpu_seconds_total{mode="user"}[5m])) * 100'),
createPanel("Memory Usage", '(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100'),
],
},
}
這個 Jsonnet 設定展示了幾個關鍵的改進:
dashboard
變數作為基礎模板。createPanel
函數封裝了 Panel 的通用結構,大大減少了重複程式碼。createPanel
函數來添加新的 Panel,而不需要複製粘貼大塊程式碼。createPanel
函數,而不是逐個修改每個 Panel。這個簡單的例子展示了 Jsonnet 如何通過引入程式語言的特性來增強 JSON 的表達能力。在實際應用中,Jsonnet 的優勢會更加明顯,特別是在處理大型、複雜的設定時。
Jsonnet 在監控領域,特別是在 Grafana 生態系統中,已經成為一個不可或缺的工具。它的應用主要體現在兩個方面:Prometheus Monitoring Mixins 和 Grafonnet。讓我們深入探討這兩個領域,看看 Jsonnet 如何革新了監控設定的管理方式。
Prometheus Monitoring Mixins 是一種使用 Jsonnet 來定義可重用監控組件的方法。這個概念最初由 Grafana Labs 提出,旨在解決大規模監控系統中的設定管理問題。Mixins 允許團隊創建、共享和組合監控設定,包括告警規則、記錄規則和 Grafana Dashboard。
Mixins 的核心思想是:
例如,一個典型的 Prometheus Mixin 可能包含以下部分:
{
prometheusAlerts+:: {
groups+: [
{
name: 'example-alerts',
rules: [
{
alert: 'HighErrorRate',
expr: 'rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.1',
'for': '10m',
labels: {
severity: 'warning',
},
annotations: {
summary: 'High error rate detected',
description: 'Error rate is above 10% for the last 10 minutes',
},
},
],
},
],
},
}
這個例子定義了一個簡單的告警規則。使用 Jsonnet,我們可以輕鬆地將這個規則與其他 Mixins 組合,或者根據需要進行覆蓋和定制。
Mixins 的優勢在於:
Grafonnet 是一個專門用於生成 Grafana Dashboard 的 Jsonnet 庫。它提供了一組進階抽象介面,使得創建和管理複雜的 Grafana Dashboard 變得更加簡單和系統化。
使用 Grafonnet,開發者可以用程式化的方式定義 Dashboard,從而實現更好的版本控制和自動化。以下是一個使用 Grafonnet 創建 Dashboard 的簡單例子:
local grafana = import 'grafonnet/grafana.libsonnet';
local dashboard = grafana.dashboard;
local row = grafana.row;
local prometheus = grafana.prometheus;
local graph = grafana.graphPanel;
dashboard.new(
'My Dashboard',
tags=['generated', 'jsonnet'],
time_from='now-1h',
)
.addRow(
row.new()
.addPanel(
graph.new(
'HTTP Requests',
datasource='Prometheus',
span=12,
format='short',
min=0,
)
.addTarget(
prometheus.target(
'sum(rate(http_requests_total[5m])) by (status_code)',
legendFormat='{{status_code}}',
)
)
)
)
這個例子展示了如何使用 Grafonnet 創建一個包含 HTTP 請求圖表的 Dashboard。Grafonnet 的優勢包括:
Grafonnet 的使用不僅簡化了 Dashboard 的創建過程,還大大提高了維護效率。例如,如果需要在所有 Dashboard 中更新某個共同的元素(如資料源或時間範圍),只需修改一處程式碼,然後重新生成所有 Dashboard 即可。
我們可以想像團隊使用 Jsonnet 後的相關情況在渡過前期的學習取線後,我們所使用到的設定檔體積將會大幅精簡,每個經手開發的人員將會更了解現有資源,並且進一步的抽象復用,形成一個良性循環。
指標 | 使用前 | 使用後 |
---|---|---|
設定檔大小 (KB) | 500 | 300 |
維護時間 (小時/月) | 20 | 10 |
錯誤率 (%) | 5 | 1 |
Jsonnet 在 Grafana 生態系統中的應用,特別是通過 Prometheus Monitoring Mixins 和 Grafonnet,極大地改善了監控設定的管理方式。它不僅解決了 JSON 的局限性,還為團隊提供了一種更加靈活、可維護的方法來處理複雜的監控設置。
隨著監控需求的不斷增長和複雜化,Jsonnet 的重要性只會越來越突出。它為 Grafana as Code 的實踐提供了強大的支持,使得大規模、高效率的監控設定管理成為可能。
對於任何希望提升監控效率和可維護性的團隊來說,掌握 Jsonnet 都是一項值得投資的技能。它不僅能夠解決當前的設定管理問題,還能為未來的擴展和優化奠定堅實的基礎。
在未來,我們可以期待看到更多基於 Jsonnet 的工具和庫的出現,進一步豐富 Grafana 生態系統。同時,隨著更多團隊採用這種方法,社區中的最佳實踐和共享資源也將不斷增加,使得高質量的監控設定變得更加容易實現和維護。
總的來說,Jsonnet 在 Grafana 和監控領域的應用,代表了設定管理的一次重要革新。它不僅解決了當前的痛點,還為未來的發展開闢了新的可能性。對於任何嚴肅對待監控的組織來說,深入了解和採用 Jsonnet 都是一個明智的選擇。
References:
https://github.com/monitoring-mixins/docs#guidelines-for-alert-names-labels-and-annotations
https://grafana.com/blog/2020/02/26/how-to-configure-grafana-as-code/